@@ -2,6 +2,64 @@ Revision history for Perl extension Plack
Take a look at http://github.com/miyagawa/Plack/issues for the planned changes before 1.0 release.
+0.9932 Mon Apr 19 15:23:55 JST 2010
+ - Enable Lint middleware by default in the development env
+ - Lint middleware now validates $app on startup
+ - Fixed documentations on middleware and handlers
+
+0.9931 Fri Apr 16 23:52:27 PDT 2010
+ - replace kyoto.jpg test image file with smaller baybridge.jpg to strip down the tarball size
+ from 2.5MB to 212KB.
+
+0.9930 Tue Apr 13 20:18:06 PDT 2010
+ - Added Plack::Handler::Net::FastCGI (chansen)
+ - Made Test::TCP a hard dependency since Plack::Test needs it
+ - Added Delayed loader for Starlet and Starman (clkao)
+ - Hide logger middleware from log4perl's caller stack (haarg)
+
+0.9929 Wed Mar 31 00:33:10 PDT 2010
+ - Middleware::JSONP: Simplified code and does not support IO response body type
+ - fcgi.t: skip tests with lighttpd < 1.4.17 per CPAN Testers #7040400
+
+0.9928 Mon Mar 29 17:02:42 PDT 2010
+ - log_dispatch.t: require Log::Dispatch::Array
+
+0.9927 Mon Mar 29 12:43:44 PDT 2010
+ - require newer Log::Dispatch (confound)
+ - StackTrace: Encode exceptions in utf-8 in case they include wide characters #95 (tokuhirom)
+ - StackTrace: Depends on a new Devel::StackTrace::AsHTML that escapes wide characters
+ - StackTrace: Display stacktrace only if the thrown exception is a direct error #91 (frodwith)
+ - StackTrace: Added 'force' option to force stacktrace in 500 errors
+ - Avoid warnings when response_cb filter returns undef in ARRAY response body #92 (hiratara)
+ - URLMap: Ignore port number if it matches with SERVER_PORT #90 (omega)
+ - URLMap: Enable debug print with PLACK_URLMAP_DEBUG=1 #94
+ - JSONP: Fixed possible infinite-loop when using with IO response body (hiratara)
+ - Fixed the compatiblity issues with FastCGI docs and tests with lighttpd 1.4.26 (tadam)
+ - LighttpdScriptNameFix: Added 'script_name' option (tadam)
+
+0.9926 Sun Mar 28 14:37:03 PDT 2010
+ - Added -v|--version option to plackup and the ability for Plack::Runner users to override
+
+0.9925 Sat Mar 27 19:03:57 PDT 2010
+ - Make this a non-devel release
+
+0.99_24 Sat Mar 27 13:31:51 PDT 2010
+ - Disable Devel::StackTrace::WithLexicals for now until PadWalker RT #55242 is fixed
+
+0.99_23 Sat Mar 27 01:02:24 PDT 2010
+ - Dropped keep-alive code from HTTP::Server::PSGI now that Starlet clones the code
+ - Special case --disable-* and --enable-* command line options in plackup and Plack::Runner
+
+0.99_22 Thu Mar 25 19:48:08 PDT 2010
+ - INCOMPATIBLE: removed --max-workers option from the default standalone server.
+ Now it gives you warnings and falls back to the single process mode.
+
+0.99_21 Thu Mar 25 15:05:53 PDT 2010
+ - INCOMPATIBLE: removed a workaround for lighttpd SCRIPT_NAME bug in FCGI handler
+ See http://github.com/miyagawa/Plack/issues#issue/68 for details.
+ - HTTPException now logs standard exceptions to psgi.errors
+ - micro optimization for Plack::Request content() method
+
0.9920 Thu Mar 18 23:48:06 PDT 2010
- Fixed URL path prefix matching in URLMap (hiratara)
- Fixed Plack::Request->content on GET with FastCGI servers (sunnavy)
@@ -65,10 +65,12 @@ lib/Plack/Handler/CGI.pm
lib/Plack/Handler/FCGI.pm
lib/Plack/Handler/HTTP/Server/PSGI.pm
lib/Plack/Handler/HTTP/Server/Simple.pm
+lib/Plack/Handler/Net/FastCGI.pm
lib/Plack/Handler/Standalone.pm
lib/Plack/HTTPParser.pm
lib/Plack/HTTPParser/PP.pm
lib/Plack/Loader.pm
+lib/Plack/Loader/Delayed.pm
lib/Plack/Loader/Restarter.pm
lib/Plack/Loader/Shotgun.pm
lib/Plack/Middleware.pm
@@ -85,6 +87,7 @@ lib/Plack/Middleware/ErrorDocument.pm
lib/Plack/Middleware/Head.pm
lib/Plack/Middleware/HTTPExceptions.pm
lib/Plack/Middleware/JSONP.pm
+lib/Plack/Middleware/LighttpdScriptNameFix.pm
lib/Plack/Middleware/Lint.pm
lib/Plack/Middleware/Log4perl.pm
lib/Plack/Middleware/LogDispatch.pm
@@ -126,12 +129,12 @@ lib/Plack/Test/Suite.pm
lib/Plack/Util.pm
lib/Plack/Util/Accessor.pm
Makefile.PL
-MANIFEST
+MANIFEST This list of files
META.yml
README
scripts/plackup
+share/baybridge.jpg
share/face.jpg
-share/kyoto.jpg
t/00_compile.t
t/FCGIUtils.pm
t/HTTP-Message-PSGI/utf8_req.t
@@ -143,11 +146,12 @@ t/Plack-Handler/cgi.t
t/Plack-Handler/fcgi.t
t/Plack-Handler/fcgi_client.t
t/Plack-Handler/http_server_simple.t
-t/Plack-Handler/standalone-prefork.t
+t/Plack-Handler/net_fastcgi.t
t/Plack-Handler/standalone.t
t/Plack-HTTPParser-PP/simple.t
t/Plack-Loader/auto.t
t/Plack-Loader/auto_fallback.t
+t/Plack-Loader/delayed.t
t/Plack-Loader/shotgun.t
t/Plack-Middleware/access_log.t
t/Plack-Middleware/access_log_timed.t
@@ -162,6 +166,7 @@ t/Plack-Middleware/cgi-bin/hello3.cgi
t/Plack-Middleware/cgibin.t
t/Plack-Middleware/chunked.t
t/Plack-Middleware/component-leak.t
+t/Plack-Middleware/component.t
t/Plack-Middleware/conditional.t
t/Plack-Middleware/conditionalget.t
t/Plack-Middleware/content_length.t
@@ -190,12 +195,16 @@ t/Plack-Middleware/runtime.t
t/Plack-Middleware/simple_content_filter.t
t/Plack-Middleware/simple_logger.t
t/Plack-Middleware/stacktrace.t
+t/Plack-Middleware/stacktrace_force.t
+t/Plack-Middleware/stacktrace_streaming.t
+t/Plack-Middleware/stacktrace_utf8.t
t/Plack-Middleware/static.t
t/Plack-Middleware/static.txt
t/Plack-Middleware/static_env.t
t/Plack-Middleware/urlmap.t
t/Plack-Middleware/urlmap_builder.t
t/Plack-Middleware/urlmap_env.t
+t/Plack-Middleware/urlmap_ports.t
t/Plack-Middleware/wrapcgi.t
t/Plack-Middleware/xframework.t
t/Plack-Middleware/xsendfile.t
@@ -1,12 +1,11 @@
---
abstract: 'Perl Superglue for Web frameworks and Web Servers (PSGI toolkit)'
author:
- - '- Tatsuhiko Miyagawa'
+ - 'Tatsuhiko Miyagawa'
build_requires:
ExtUtils::MakeMaker: 6.42
Test::More: 0.88
Test::Requires: 0
- Test::TCP: 0.11
configure_requires:
ExtUtils::MakeMaker: 6.42
distribution_type: module
@@ -24,13 +23,14 @@ no_index:
- xt
requires:
Devel::StackTrace: 0
- Devel::StackTrace::AsHTML: 0
+ Devel::StackTrace::AsHTML: 0.09
File::ShareDir: 1.00
Filesys::Notify::Simple: 0
HTTP::Body: 1.06
Hash::MultiValue: 0.05
LWP: 5.814
Pod::Usage: 0
+ Test::TCP: 0.11
Try::Tiny: 0
URI: 1.36
parent: 0
@@ -38,4 +38,4 @@ requires:
resources:
license: http://dev.perl.org/licenses/
repository: git://github.com/miyagawa/Plack.git
-version: 0.9920
+version: 0.9932
@@ -9,26 +9,26 @@ name 'Plack';
all_from 'lib/Plack.pm';
readme_from 'lib/Plack.pm';
-requires 'LWP', 5.814; # HTTP::Status, HTTP::Headers and HTTP::Request
-requires 'URI', 1.36;
-requires 'Pod::Usage'; # plackup
-requires 'File::ShareDir', '1.00'; # Plack::Test::Suite
+requires 'LWP', 5.814; # HTTP::Status, HTTP::Headers and HTTP::Request
+requires 'URI', 1.36; # URI::Escape
+requires 'Pod::Usage'; # plackup
+requires 'File::ShareDir', '1.00'; # Plack::Test::Suite
requires 'Try::Tiny';
requires 'parent';
-requires 'Devel::StackTrace'; # Middleware::StackTrace
-requires 'Devel::StackTrace::AsHTML'; # Middleware::StackTrace
+requires 'Devel::StackTrace'; # Middleware::StackTrace
+requires 'Devel::StackTrace::AsHTML', 0.09; # Middleware::StackTrace
-requires 'Filesys::Notify::Simple'; # plackup -r
+requires 'Filesys::Notify::Simple'; # plackup -r
-requires 'Hash::MultiValue', 0.05; # Plack::Request
-requires 'HTTP::Body', 1.06; # Plack::Request
+requires 'Hash::MultiValue', 0.05; # Plack::Request
+requires 'HTTP::Body', 1.06; # Plack::Request
+requires 'Test::TCP', 0.11; # Plack::Test needs to be installed
build_requires 'Test::More', 0.88;
-build_requires 'Test::TCP', 0.11;
test_requires 'Test::Requires';
-tests 't/*.t t/*/*.t t/*/*/*.t t/*/*/*/*.t t/*/*/*/*/*.t';
+tests 't/*.t t/*/*.t';
author_tests 'xt';
install_share 'share';
@@ -12,14 +12,17 @@ DESCRIPTION
MODULES AND UTILITIES
Plack::Handler
Plack::Handler and its subclasses contains adapters for web servers. We
- have adapters for Standalone, CGI, FCGI, Apache, AnyEvent, Coro,
- Danga::Socket and many server environments that you can run PSGI
- applications on.
+ have adapters for the built-in standalone web server HTTP::Server::PSGI,
+ CGI, FCGI, Apache1, Apache2, Net::FastCGI and HTTP::Server::Simple
+ included in the core Plack distribution.
+
+ There are also many HTTP server implementations on CPAN that has Plack
+ handlers.
See Plack::Handler how to write your own adapters.
Plack::Loader
- Plack::Loader is a loader to load one of Plack::Server adapters and run
+ Plack::Loader is a loader to load one of Plack::Handler adapters and run
PSGI application code reference with it.
Plack::Util
@@ -90,8 +93,13 @@ MODULES AND UTILITIES
CONTRIBUTING
Patches and Bug Fixes
Small patches and bug fixes can be either submitted via nopaste on IRC
- <irc://irc.perl.org/#plack> or email. You could also fork on github
- (http://github.com/miyagawa/Plack) to make larger fixes.
+ <irc://irc.perl.org/#plack> or the github issue tracker
+ <http://github.com/miyagawa/Plack/issues>. Forking on github
+ <http://github.com/miyagawa/Plack> is another good way if you intend to
+ make larger fixes.
+
+ See also <http://contributing.appspot.com/plack> when you think this
+ document is terribly outdated.
Module Namespaces
Modules added to the Plack:: sub-namespaces should be reasonably generic
@@ -117,12 +125,12 @@ CONTRIBUTING
it's supposed to run on CGI and that is a really bad choice and confuse
people.
-COPYRIGHT
- Copyright 2009- Tatsuhiko Miyagawa
-
AUTHOR
Tatsuhiko Miyagawa
+COPYRIGHT
+ Copyright 2009-2010 Tatsuhiko Miyagawa
+
CONTRIBUTORS
Yuval Kogman (nothingmuch)
@@ -130,7 +138,7 @@ CONTRIBUTORS
Kazuhiro Osawa (Yappo)
- Kzzuho Oku
+ Kazuho Oku
Florian Ragwitz (rafl)
@@ -17,12 +17,12 @@ my $url = 'http://127.0.0.1/';
my @try = (
[ 'AnyEvent::HTTPD' ],
[ 'HTTP::Server::PSGI' ],
- [ 'HTTP::Server::PSGI', ' (workers=10)', max_workers => 10 ],
[ 'Twiggy' ],
[ 'HTTP::Server::Simple' ],
[ 'Corona' ],
[ 'Danga::Socket' ],
- [ 'POE' ],
+ [ '+POE::Component::Server::PSGI' ],
+ [ 'Starlet', ' (workers=10)', max_workers => 10 ],
[ 'Starman', ' (workers=10)', workers => 10 ],
);
@@ -1,5 +1,5 @@
use File::Basename;
-my $path = $ENV{PSGI_IMAGE_FILE} || dirname(__FILE__) . "/../../share/kyoto.jpg";
+my $path = $ENV{PSGI_IMAGE_FILE} || dirname(__FILE__) . "/../../share/baybridge.jpg";
my $handler = sub {
open my $fh, "<:raw", $path or die $!;
return [ 200, [ "Content-Type" => "image/jpeg", "X-Sendfile" => $path, "Content-Length" => -s $fh ], $fh ];
@@ -113,7 +113,7 @@ sub _res_from_psgi {
$res->headers->header(@$headers) if @$headers;
if (ref $body eq 'ARRAY') {
- $res->content(join '', @$body);
+ $res->content(join '', grep defined, @$body);
} else {
local $/ = \4096;
my $content;
@@ -2,6 +2,7 @@ package HTTP::Server::PSGI;
use strict;
use warnings;
+use Carp ();
use Plack;
use Plack::HTTPParser qw( parse_http_request );
use IO::Socket::INET;
@@ -37,27 +38,16 @@ sub new {
host => $args{host} || 0,
port => $args{port} || 8080,
timeout => $args{timeout} || 300,
- keepalive_timeout => $args{keepalive_timeout} || 2,
- max_keepalive_reqs => $args{max_keepalive_reqs},
server_software => $args{server_software} || $class,
server_ready => $args{server_ready} || sub {},
- max_workers => $args{max_workers} || 1,
max_reqs_per_child => $args{max_reqs_per_child} || 100,
}, $class;
- if ($self->{max_workers} > 1) {
- try {
- require Parallel::Prefork;
- $self->{prefork} = 1;
- $self->{max_keepalive_reqs} ||= 100;
- $self->{server_software} .= " (prefork)";
- } catch {
- die "You need to install Parallel::Prefork to run multi workers (max_workers=$self->{max_workers}): $_";
- };
- }
-
- unless ($self->{prefork}) {
- $self->{max_keepalive_reqs} ||= 1;
+ if ($args{max_workers} && $args{max_workers} > 1) {
+ Carp::carp(
+ "Preforking in $class is deprecated. Falling back to the non-forking mode. ",
+ "If you need preforking, use Starman or Starlet instead and run like `plackup -s Starlet`",
+ );
}
$self;
@@ -66,12 +56,7 @@ sub new {
sub run {
my($self, $app) = @_;
$self->setup_listener();
-
- if ($self->{prefork}) {
- $self->run_prefork($app);
- } else {
- $self->accept_loop($app);
- }
+ $self->accept_loop($app);
}
sub setup_listener {
@@ -87,24 +72,6 @@ sub setup_listener {
$self->{server_ready}->($self);
}
-sub run_prefork {
- my($self, $app) = @_;
-
- my $pm = Parallel::Prefork->new({
- max_workers => $self->{max_workers},
- trap_signals => {
- TERM => 'TERM',
- HUP => 'TERM',
- },
- });
- while ($pm->signal_received ne 'TERM') {
- $pm->start and next;
- $self->accept_loop($app, $self->{max_reqs_per_child});
- $pm->finish;
- }
- $pm->wait_all_children;
-}
-
sub accept_loop {
# TODO handle $max_reqs_per_child
my($self, $app, $max_reqs_per_child) = @_;
@@ -117,43 +84,32 @@ sub accept_loop {
if (my $conn = $self->{listen_sock}->accept) {
$conn->setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
or die "setsockopt(TCP_NODELAY) failed:$!";
- my $req_count = 0;
- while (1) {
- ++$req_count;
- ++$proc_req_count;
- my $env = {
- SERVER_PORT => $self->{port},
- SERVER_NAME => $self->{host},
- SCRIPT_NAME => '',
- REMOTE_ADDR => $conn->peerhost,
- 'psgi.version' => [ 1, 1 ],
- 'psgi.errors' => *STDERR,
- 'psgi.url_scheme' => 'http',
- 'psgi.run_once' => Plack::Util::FALSE,
- 'psgi.multithread' => Plack::Util::FALSE,
- 'psgi.multiprocess' => $self->{prefork},
- 'psgi.streaming' => Plack::Util::TRUE,
- 'psgi.nonblocking' => Plack::Util::FALSE,
- 'psgix.input.buffered' => Plack::Util::TRUE,
- 'psgix.io' => $conn,
- };
-
- # no need to take care of pipelining since this module is a HTTP/1.0 server
- my $may_keepalive = $req_count < $self->{max_keepalive_reqs};
- if ($may_keepalive && $max_reqs_per_child && $proc_req_count >= $max_reqs_per_child) {
- $may_keepalive = undef;
- }
- $self->handle_connection($env, $conn, $app, $may_keepalive, $req_count != 1)
- or last;
- # TODO add special cases for clients with broken keep-alive support, as well as disabling keep-alive for HTTP/1.0 proxies
- }
+ ++$proc_req_count;
+ my $env = {
+ SERVER_PORT => $self->{port},
+ SERVER_NAME => $self->{host},
+ SCRIPT_NAME => '',
+ REMOTE_ADDR => $conn->peerhost,
+ 'psgi.version' => [ 1, 1 ],
+ 'psgi.errors' => *STDERR,
+ 'psgi.url_scheme' => 'http',
+ 'psgi.run_once' => Plack::Util::FALSE,
+ 'psgi.multithread' => Plack::Util::FALSE,
+ 'psgi.multiprocess' => Plack::Util::FALSE,
+ 'psgi.streaming' => Plack::Util::TRUE,
+ 'psgi.nonblocking' => Plack::Util::FALSE,
+ 'psgix.input.buffered' => Plack::Util::TRUE,
+ 'psgix.io' => $conn,
+ };
+
+ $self->handle_connection($env, $conn, $app);
$conn->close;
}
}
}
sub handle_connection {
- my($self, $env, $conn, $app, $use_keepalive, $is_keepalive) = @_;
+ my($self, $env, $conn, $app) = @_;
my $buf = '';
my $res = [ 400, [ 'Content-Type' => 'text/plain' ], [ 'Bad Request' ] ];
@@ -161,19 +117,10 @@ sub handle_connection {
while (1) {
my $rlen = $self->read_timeout(
$conn, \$buf, MAX_REQUEST_SIZE - length($buf), length($buf),
- $is_keepalive ? $self->{keepalive_timeout} : $self->{timeout},
+ $self->{timeout},
) or return;
my $reqlen = parse_http_request($buf, $env);
if ($reqlen >= 0) {
- # handle request
- if ($use_keepalive) {
- if (my $c = $env->{HTTP_CONNECTION}) {
- $use_keepalive = undef
- unless $c =~ /^\s*keep-alive\s*/i;
- } else {
- $use_keepalive = undef;
- }
- }
$buf = substr $buf, $reqlen;
if (my $cl = $env->{CONTENT_LENGTH}) {
my $buffer = Plack::TempBuffer->new($cl);
@@ -206,20 +153,20 @@ sub handle_connection {
}
if (ref $res eq 'ARRAY') {
- $self->_handle_response($res, $conn, \$use_keepalive);
+ $self->_handle_response($res, $conn);
} elsif (ref $res eq 'CODE') {
$res->(sub {
- $self->_handle_response($_[0], $conn, \$use_keepalive);
+ $self->_handle_response($_[0], $conn);
});
} else {
die "Bad response $res";
}
- return $use_keepalive;
+ return;
}
sub _handle_response {
- my($self, $res, $conn, $use_keepalive_r) = @_;
+ my($self, $res, $conn) = @_;
my @lines = (
"Date: @{[HTTP::Date::time2str()]}\015\012",
@@ -228,19 +175,9 @@ sub _handle_response {
Plack::Util::header_iter($res->[1], sub {
my ($k, $v) = @_;
- if (lc $k eq 'connection') {
- $$use_keepalive_r = undef
- if $$use_keepalive_r && lc $v ne 'keep-alive';
- } else {
- push @lines, "$k: $v\015\012";
- }
+ push @lines, "$k: $v\015\012";
});
- if ($$use_keepalive_r) {
- $$use_keepalive_r = undef
- unless Plack::Util::header_exists($res->[1], 'Content-Length');
- }
- push @lines, "Connection: keep-alive\015\012"
- if $$use_keepalive_r;
+
unshift @lines, "HTTP/1.0 $res->[0] @{[ HTTP::Status::status_message($res->[0]) ]}\015\012";
push @lines, "\015\012";
@@ -348,8 +285,7 @@ HTTP::Server::PSGI - Standalone PSGI compatible HTTP server
=head1 DESCRIPTION
HTTP::Server::PSGI is a standalone, single-process and PSGI compatible
-HTTP server implementations. It runs reasonably fast and HTTP/1.0 and
-Keep-Alive requests are supported.
+HTTP server implementations.
This server should be great for the development and testig, but might
not be suitable for production.
@@ -357,8 +293,10 @@ not be suitable for production.
Some features in HTTP/1.1, notably chunked requests, responses and
pipeline requests are B<NOT> supported yet.
-See L<Starman> if you want a multi-process prefork server with some
-HTTP/1.1 features support.
+=head1 PREFORKING
+
+L<HTTP::Server::PSGI> does B<NOT> support preforking. See L<Starman>
+or L<Starlet> if you want a multi-process prefork web servers.
=head1 AUTHOR
@@ -368,6 +306,6 @@ Tatsuhiko Miyagawa
=head1 SEE ALSO
-L<Plack::Handler::Standalone>
+L<Plack::Handler::Standalone> L<Starman> L<Starlet>
=cut
@@ -2,6 +2,7 @@ package Plack::App::URLMap;
use strict;
use warnings;
use parent qw(Plack::Component);
+use constant DEBUG => $ENV{PLACK_URLMAP_DEBUG};
use Carp ();
@@ -43,15 +44,21 @@ sub call {
my($http_host, $server_name) = @{$env}{qw( HTTP_HOST SERVER_NAME )};
+ if ($http_host and my $port = $env->{SERVER_PORT}) {
+ $http_host =~ s/:$port$//;
+ }
+
for my $map (@{ $self->{_sorted_mapping} }) {
my($host, $location, $app) = @$map;
my $path = $path_info; # copy
no warnings 'uninitialized';
+ DEBUG && warn "Matching request (Host=$http_host Path=$path) and the map (Host=$host Path=$location)\n";
next unless not defined $host or
$http_host eq $host or
$server_name eq $host;
next unless $location eq '' or $path =~ s!^\Q$location\E!!;
next unless $path eq '' or $path =~ m!^/!;
+ DEBUG && warn "-> Matched!\n";
my $orig_path_info = $env->{PATH_INFO};
my $orig_script_name = $env->{SCRIPT_NAME};
@@ -64,6 +71,8 @@ sub call {
});
}
+ DEBUG && warn "All matching failed.\n";
+
return [404, [ 'Content-Type' => 'text/plain' ], [ "Not Found" ]];
}
@@ -132,6 +141,12 @@ should also work.
=back
+=head1 DEBUGGING
+
+You can set the environment variable C<PLACK_URLMAP_DEBUG> to see how
+this application matches with the incoming request host names and
+paths.
+
=head1 AUTHOR
Tatsuhiko Miyagawa
@@ -55,7 +55,8 @@ sub response_cb {
$line = $filter_cb->($line);
}
# Send EOF.
- push @{ $res->[2] }, $filter_cb->( undef );
+ my $eof = $filter_cb->( undef );
+ push @{ $res->[2] }, $eof if defined $eof;
} else {
my $body = $res->[2];
my $getline = sub { $body->getline };
@@ -136,12 +136,23 @@ Plack::Handler::Apache2 - Apache 2.0 handlers to run PSGI application
PerlSetVar psgi_app /path/to/app.psgi
</Location>
+ # Optional, preload the application in the parent like startup.pl
<Perl>
use Plack::Handler::Apache2;
Plack::Handler::Apache2->preload("/path/to/app.psgi");
</Perl>
- # Or create your own handler:
+=head1 DESCRIPTION
+
+This is a handler module to run any PSGI application with mod_perl on Apache 2.x.
+
+=head1 CREATING CUSTOM HANDLER
+
+If you want to create a custom handler that loads or creates PSGI
+applications using other means than loading from C<.psgi> files, you
+can create your own handler class and use C<call_app> class method to
+run your application.
+
package My::ModPerl::Handler;
use Plack::Handler::Apache2;
@@ -155,19 +166,6 @@ Plack::Handler::Apache2 - Apache 2.0 handlers to run PSGI application
Plack::Handler::Apache2->call_app($r, $app);
}
-=head1 DESCRIPTION
-
-This is a handler module to run any PSGI application with mod_perl on Apache 2.x.
-
-=head1 METHODS
-
-=head2 call_app($r, $app)
-
-The mod_perl handler in this package loads the app and calls this method. If
-you'd like to do something different, you can still make use of this module by
-preparing $r and $app in some other fashion and calling this as a class
-method.
-
=head1 AUTHOR
Tatsuhiko Miyagawa
@@ -87,6 +87,10 @@ package Plack::Handler::CGI;
1;
__END__
+=head1 NAME
+
+Plack::Handler::CGI - CGI handler for Plack
+
=head1 SYNOPSIS
Want to run PSGI application as a CGI script? Rename .psgi to .cgi and
@@ -52,7 +52,7 @@ sub run {
my $proc_manager;
if ($self->{listen}) {
- $self->daemon_fork if $self->{detach};
+ $self->daemon_fork if $self->{daemonize};
if ($self->{manager}) {
Plack::Util::load_class($self->{manager});
@@ -88,12 +88,15 @@ sub run {
'psgi.nonblocking' => Plack::Util::FALSE,
};
- # If we're running under Lighttpd, swap PATH_INFO and SCRIPT_NAME if PATH_INFO is empty
- # http://lists.rawmode.org/pipermail/catalyst/2006-June/008361.html
- # Thanks to Mark Blythe for this fix
- if ($env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ /lighttpd/) {
- $env->{PATH_INFO} ||= delete $env->{SCRIPT_NAME};
- $env->{SCRIPT_NAME} ||= '';
+ if ($env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ m!lighttpd[-/]1\.(\d+\.\d+)!) {
+ no warnings;
+ if ($ENV{PLACK_ENV} eq 'development' && $1 < 4.23 && $env->{PATH_INFO} eq '') {
+ warn "You're using lighttpd 1.$1 and appear to mount your FastCGI handler under the root ('/'). ",
+ "It's known to be causing issues because of the lighttpd bug. You're recommended to enable ",
+ "LighttpdScriptNameFix middleware, or upgrade lighttpd to 1.4.23 or later and include ",
+ "'fix-root-scriptname' flag in 'fastcgi.server'. See perldoc Plack::Handler::FCGI for details. ",
+ "This friendly warning will go away in the next major release of Plack.";
+ }
$env->{SERVER_NAME} =~ s/:\d+$//; # cut off port number
}
@@ -161,6 +164,10 @@ sub daemon_detach {
__END__
+=head1 NAME
+
+Plack::Handler::FCGI - FastCGI handler for Plack
+
=head1 SYNOPSIS
# Run as a standalone daemon
@@ -276,15 +283,24 @@ See L<http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html#FastCgiExternalSe
=head3 lighttpd
-Host in the root path:
+To host the app in the root path, you're recommended to use lighttpd
+1.4.23 or newer with C<fix-root-scriptname> flag like below.
- fastcgi.server = ( "" =>
+ fastcgi.server = ( "/" =>
((
"socket" => "/tmp/fcgi.sock",
"check-local" => "disable"
+ "fix-root-scriptname" => "enable",
))
-Or in the non-root path over TCP:
+If you use lighttpd older than 1.4.22 where you don't have
+C<fix-root-scriptname>, mouting apps under the root causes wrong
+C<SCRIPT_NAME> and C<PATH_INFO> set. Also, mouting under the empty
+root (C<"">) or a path that has a trailing slash would still cause
+weird values set even with C<fix-root-scriptname>. In such cases you
+can use L<Plack::Middleware::LighttpdScriptNameFix> to fix it.
+
+To mount in the non-root path over TCP:
fastcgi.server = ( "/foo" =>
((
@@ -293,9 +309,10 @@ Or in the non-root path over TCP:
"check-local" => "disable"
))
-Plack::Handler::FCGI has a workaround for lighttpd's weird
-C<SCRIPT_NAME> and C<PATH_INFO> setting when you set I<check-local> to
-C<disable> so both configurations (root or non-root) should work fine.
+It's recommended that your mount path does B<NOT> have the trailing
+slash. If you I<really> need to have one, you should consider using
+L<Plack::Middleware::LighttpdScriptNameFix> to fix the wrong
+B<PATH_INFO> values set by lighttpd.
=cut
@@ -32,11 +32,13 @@ Plack::Handler::HTTP::Server::PSGI - adapter for HTTP::Server::PSGI
% plackup -s HTTP::Server::PSGI \
--host 127.0.0.1 --port 9091 --timeout 120
-=head1 CONFIGURATIONS
+=head1 BACKWARD COMPATIBLITY
+
+Since Plack 0.99_22 this handler doesn't support preforking
+configuration i.e. C<--max-workers>. Use L<Starman> or L<Starlet> if
+you need preforking PSGI web server.
-This adapter automatically loads Prefork implementation when
-C<max-workers> is set, but otherwise the default HTTP::Server::PSGI
-which is single process.
+=head1 CONFIGURATIONS
=over 4
@@ -52,26 +54,10 @@ Port number the server listens on. Defaults to 8080.
Number of seconds a request times out. Defaults to 300.
-=item max-keepalive-reqs
-
-Max requests per a keep-alive request. Defaults to 1, which means Keep-alive is off.
-
-=item keepalive-timeout
-
-Number of seconds a keep-alive request times out. Defaults to 2.
-
-=item max-workers
-
-Number of prefork workers. Defaults to 10.
-
=item max-reqs-per-child
Number of requests per worker to process. Defaults to 100.
-=item max-keepalive-reqs
-
-Max requests per a keep-alive request. Defaults to 100.
-
=back
=head1 AUTHOR
@@ -0,0 +1,285 @@
+package Plack::Handler::Net::FastCGI;
+use strict;
+use Plack::Util;
+use IO::Socket qw[];
+use Net::FastCGI::Constant qw[:common :type :flag :role :protocol_status];
+use Net::FastCGI::Protocol qw[:all];
+
+sub DEBUG () { 0 }
+
+sub new {
+ my $class = shift;
+ my $self = bless { @_ }, $class;
+ $self->{listen} ||= [ ":$self->{port}" ] if $self->{port};
+ $self->{values} ||= {
+ FCGI_MAX_CONNS => 1, # maximum number of concurrent transport connections this application will accept
+ FCGI_MAX_REQS => 1, # maximum number of concurrent requests this application will accept
+ FCGI_MPXS_CONNS => 0, # this implementation can't multiplex
+ };
+ $self;
+}
+
+sub run {
+ my ($self, $app) = @_;
+ $self->{app} = $app;
+
+ my $socket;
+ my $proto;
+ my $port;
+
+ if ($self->{listen}) {
+ $port = $self->{listen}->[0];
+ if ($port =~ s/^://) {
+ $proto = 'tcp';
+ $socket = IO::Socket::INET->new(
+ Listen => 5,
+ LocalPort => $port,
+ Reuse => 1
+ ) or die "Couldn't create listener socket: $!";
+ } else {
+ $proto = 'unix';
+ $socket = IO::Socket::UNIX->new(
+ Listen => 5,
+ Local => $port,
+ ) or die "Couldn't create UNIX listener socket: $!";
+ }
+ }
+ else {
+ (-S STDIN)
+ || die "Standard input is not a socket: specify a listen location";
+ $socket = \*STDIN;
+ $socket->autoflush(1);
+ }
+
+ $self->{server_ready}->({
+ host => 'localhost',
+ port => $port,
+ proto => $proto,
+ server_software => 'Plack::Handler::Net::FastCGI',
+ }) if $self->{server_ready} && $proto;
+
+ while (my $c = $socket->accept) {
+ $self->process_connection($c);
+ }
+}
+
+sub process_request {
+ my($self, $env, $stdin, $stdout, $stderr) = @_;
+
+ $env = {
+ %$env,
+ 'psgi.version' => [1,1],
+ 'psgi.url_scheme' => ($env->{HTTPS}||'off') =~ /^(?:on|1)$/i ? 'https' : 'http',
+ 'psgi.input' => $stdin,
+ 'psgi.errors' => $stderr,
+ 'psgi.multithread' => Plack::Util::FALSE,
+ 'psgi.multiprocess' => Plack::Util::FALSE, # xxx?
+ 'psgi.run_once' => Plack::Util::FALSE,
+ 'psgi.streaming' => Plack::Util::TRUE,
+ 'psgi.nonblocking' => Plack::Util::FALSE,
+ };
+
+ my $res = Plack::Util::run_app $self->{app}, $env;
+
+ if (ref $res eq 'ARRAY') {
+ $self->_handle_response($res, $stdout);
+ } elsif (ref $res eq 'CODE') {
+ $res->(sub {
+ $self->_handle_response($_[0], $stdout);
+ });
+ } else {
+ die "Bad response $res";
+ }
+}
+
+sub _handle_response {
+ my ($self, $res, $stdout) = @_;
+
+ my $hdrs;
+ $hdrs = "Status: $res->[0]\015\012";
+
+ my $headers = $res->[1];
+ while (my ($k, $v) = splice @$headers, 0, 2) {
+ $hdrs .= "$k: $v\015\012";
+ }
+ $hdrs .= "\015\012";
+
+ print {$stdout} $hdrs;
+
+ my $cb = sub { print {$stdout} $_[0] };
+ my $body = $res->[2];
+ if (defined $body) {
+ Plack::Util::foreach($body, $cb);
+ }
+ else {
+ return Plack::Util::inline_object
+ write => $cb,
+ close => sub { };
+ }
+}
+
+sub read_record {
+ @_ == 1 || die(q/Usage: read_record(io)/);
+ my ($io) = @_;
+ no warnings 'uninitialized';
+ read($io, my $header, FCGI_HEADER_LEN) == FCGI_HEADER_LEN
+ || return;
+ my ($type, $request_id, $clen, $plen) = parse_header($header);
+ (!$clen || read($io, my $content, $clen) == $clen)
+ && (!$plen || read($io, my $padding, $plen) == $plen)
+ || return;
+ $content = '' if !$clen;
+ return ($type, $request_id, $content);
+}
+
+sub process_connection {
+ my($self, $socket) = @_;
+
+ my ( $current_id, # id of the request we are currently processing
+ $stdin, # buffer for stdin
+ $stdout, # buffer for stdout
+ $stderr, # buffer for stderr
+ $params, # buffer for params (environ)
+ $output, # buffer for output
+ $done, # done with connection?
+ $keep_conn ); # more requests on this connection?
+
+ ($current_id, $stdin, $stdout, $stderr) = (0, '', '', '');
+
+ while (!$done) {
+ my ($type, $request_id, $content) = read_record($socket)
+ or last;
+
+ if (DEBUG) {
+ warn '< ', dump_record($type, $request_id, $content), "\n";
+ }
+
+ if ($request_id == FCGI_NULL_REQUEST_ID) {
+ if ($type == FCGI_GET_VALUES) {
+ my $query = parse_params($content);
+ my %reply = map { $_ => $self->{values}->{$_} }
+ grep { exists $self->{values}->{$_} }
+ keys %$query;
+ $output = build_record(FCGI_GET_VALUES_RESULT,
+ FCGI_NULL_REQUEST_ID, build_params(\%reply));
+ }
+ else {
+ $output = build_unknown_type_record($type);
+ }
+ }
+ elsif ($request_id != $current_id && $type != FCGI_BEGIN_REQUEST) {
+ # ignore inactive requests (FastCGI Specification 3.3)
+ }
+ elsif ($type == FCGI_ABORT_REQUEST) {
+ $current_id = 0;
+ ($stdin, $stdout, $stderr, $params) = ('', '', '', '');
+ }
+ elsif ($type == FCGI_BEGIN_REQUEST) {
+ my ($role, $flags) = parse_begin_request_body($content);
+ if ($current_id || $role != FCGI_RESPONDER) {
+ $output = build_end_request_record($request_id, 0,
+ $current_id ? FCGI_CANT_MPX_CONN : FCGI_UNKNOWN_ROLE);
+ }
+ else {
+ $current_id = $request_id;
+ $keep_conn = ($flags & FCGI_KEEP_CONN);
+ }
+ }
+ elsif ($type == FCGI_PARAMS) {
+ $params .= $content;
+ }
+ elsif ($type == FCGI_STDIN) {
+ $stdin .= $content;
+
+ unless (length $content) {
+ open(my $in, '<', \$stdin)
+ || die(qq/Couldn't open scalar as fh: '$!'/);
+
+ open(my $out, '>', \$stdout)
+ || die(qq/Couldn't open scalar as fh: '$!'/);
+
+ open(my $err, '>', \$stderr)
+ || die(qq/Couldn't open scalar as fh: '$!'/);
+
+ $self->process_request(parse_params($params), $in, $out, $err);
+
+ $done = 1 unless $keep_conn;
+ $output = build_end_request($request_id, 0,
+ FCGI_REQUEST_COMPLETE, $stdout, $stderr);
+
+ # prepare for next request
+ $current_id = 0;
+ ($stdin, $stdout, $stderr, $params) = ('', '', '', '');
+ }
+ }
+ else {
+ warn(qq/Received an unknown record type '$type'/);
+ }
+
+ if ($output) {
+ print {$socket} $output
+ || die(qq/Couldn't write: '$!'/);
+
+ if (DEBUG) {
+ while (length $output) {
+ my ($type, $rid, $clen, $plen) = parse_header($output);
+ my $content = substr($output, FCGI_HEADER_LEN, $clen);
+ warn '> ', dump_record($type, $rid, $content), "\n";
+ substr($output, 0, FCGI_HEADER_LEN + $clen + $plen, '');
+ }
+ }
+
+ $output = '';
+ }
+ }
+
+ if (DEBUG && !$done && $!) {
+ warn(qq/Request was prematurely aborted: '$!'/);
+ }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Plack::Handler::Net::FastCGI - FastCGI handler for Plack using Net::FastCGI
+
+=head1 SYNOPSIS
+
+ # Run as a standalone daemon using TCP port
+ plackup -s Net::FastCGI --listen :9090
+
+=head1 DESCRIPTION
+
+This is a handler module to run any PSGI application as a standalone
+FastCGI daemon using L<Net::FastCGI>
+
+=head2 OPTIONS
+
+=over 4
+
+=item listen
+
+ listen => ':8080'
+
+Listen on a socket path, hostname:port, or :port.
+
+=item port
+
+listen via TCP on port on all interfaces (Same as C<< listen => ":$port" >>)
+
+=back
+
+=head1 AUTHORS
+
+Christian Hansesn
+
+Tatsuhiko Miyagawa
+
+=head1 SEE ALSO
+
+L<Plack::Handler::FCGI>
+
+=cut
@@ -0,0 +1,70 @@
+package Plack::Loader::Delayed;
+use strict;
+use parent qw(Plack::Loader);
+
+sub preload_app {
+ my($self, $builder) = @_;
+ $self->{builder} = $builder;
+}
+
+sub run {
+ my($self, $server) = @_;
+
+ my $compiled;
+ my $app = sub {
+ $compiled ||= $self->{builder}->();
+ $compiled->(@_);
+ };
+
+ $server->{psgi_app_builder} = $self->{builder};
+ $server->run($app);
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Plack::Loader::Delayed - Delay the loading of .psgi until the first run
+
+=head1 SYNOPSIS
+
+ plackup -s Starlet -L Delayed myapp.psgi
+
+=head1 DESCRIPTION
+
+This loader delays the compilation of specified PSGI application until
+the first request time. This prevents bad things from happening with
+preforking web servers like L<Starlet>, when your application
+manipulates resources such as sockets or database connections in the
+master startup process and then shared by children.
+
+You can combine this loader with C<-M> command line option, like:
+
+ plackup -s Starlet -MCatalyst -L Delayed myapp.psgi
+
+loads the module Catalyst in the master process for the better process
+management with copy-on-write, however the application C<myapp.psgi>
+is loaded per children.
+
+L<Starman> since version 0.2000 loads this loader by default unless
+you specify the command line option C<--preload-app> for the
+L<starman> executable.
+
+=head1 DEVELOPERS
+
+Web server developers can make use of C<psgi_app_builder> attribute
+callback set in Plack::Handler, to load the application earlier than
+the first request time.
+
+=head1 AUTHOR
+
+Tatsuhiko Miyagawa
+
+=head1 SEE ALSO
+
+L<plackup>
+
+=cut
+
@@ -13,7 +13,7 @@ sub call {
my $res = try {
$self->app->($env);
} catch {
- $self->transform_error($_);
+ $self->transform_error($_, $env);
};
return $res if ref $res eq 'ARRAY';
@@ -29,7 +29,7 @@ sub call {
Carp::cluck $_;
$writer->close;
} else {
- my $res = $self->transform_error($_);
+ my $res = $self->transform_error($_, $env);
$respond->($res);
}
};
@@ -37,7 +37,7 @@ sub call {
}
sub transform_error {
- my($self, $e) = @_;
+ my($self, $e, $env) = @_;
my($code, $message);
if (blessed $e && $e->can('code')) {
@@ -47,6 +47,7 @@ sub transform_error {
overload::Method($e, '""') ? "$e" : undef;
} else {
$code = 500;
+ $env->{'psgi.errors'}->print($e);
}
if ($code !~ /^[3-5]\d\d$/) {
@@ -9,22 +9,18 @@ sub call {
my $res = $self->app->($env);
$self->response_cb($res, sub {
my $res = shift;
-
- my $h = Plack::Util::headers($res->[1]);
- if ($h->get('Content-Type') =~ m!/(?:json|javascript)! &&
+ if (defined $res->[2] && ref $res->[2] eq 'ARRAY' && @{$res->[2]} == 1) {
+ my $h = Plack::Util::headers($res->[1]);
+ if ($h->get('Content-Type') =~ m!/(?:json|javascript)! &&
$env->{QUERY_STRING} =~ /(?:^|&)callback=([^&]+)/) {
- # TODO: support callback params other than 'callback'
- my $cb = URI::Escape::uri_unescape($1);
-
- if ($cb =~ /^[\w\.\[\]]+$/) {
- $h->set('Content-Type', 'text/javascript');
-
- # The filter to transform the body into a JSONP response.
- my $isnt_first = 0;
- return sub {
- return ( $isnt_first++ ? '' : "$cb(" )
- . ( defined $_[0] ? $_[0] : ')' );
- };
+ # TODO: support callback params other than 'callback'
+ my $cb = URI::Escape::uri_unescape($1);
+ if ($cb =~ /^[\w\.\[\]]+$/) {
+ my $jsonp = "$cb($res->[2][0])";
+ $res->[2] = [ $jsonp ];
+ $h->set('Content-Length', length $jsonp);
+ $h->set('Content-Type', 'text/javascript');
+ }
}
}
});
@@ -44,7 +40,9 @@ Plack::Middleware::JSONP wraps JSON response, which has Content-Type
value either C<text/javascript> or C<application/json> as a JSONP
response which is specified with the C<callback> query parameter.
-Since this middleware removes the Content-Length header to rewrite the content body, you may also want to enable Plack::Middleware::ContentLength.
+This middleware only works with an application response with content
+body set as a single element array ref and doesn't touch the response
+otherwise.
=head1 AUTHOR
@@ -52,7 +50,7 @@ Tatsuhiko Miyagawa
=head1 SEE ALSO
-L<Plack> L<Plack::Middleware::ContentLength>
+L<Plack>
=cut
@@ -0,0 +1,93 @@
+package Plack::Middleware::LighttpdScriptNameFix;
+use strict;
+use parent qw/Plack::Middleware/;
+use Plack::Util::Accessor qw(script_name);
+
+sub prepare_app {
+ my $self = shift;
+
+ my $script_name = $self->script_name;
+ $script_name = '' unless defined($script_name);
+ $script_name =~ s!/$!!;
+ $self->script_name($script_name);
+}
+
+sub call {
+ my($self, $env) = @_;
+
+ if ($env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ /lighttpd/) {
+ $env->{PATH_INFO} = $env->{SCRIPT_NAME} . $env->{PATH_INFO};
+ $env->{SCRIPT_NAME} = $self->script_name;
+ $env->{PATH_INFO} =~ s/^\Q$env->{SCRIPT_NAME}\E//;
+ }
+
+ return $self->app->($env);
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Plack::Middleware::LighttpdScriptNameFix - fixes wrong SCRIPT_NAME and PATH_INFO that lighttpd sets
+
+=head1 SYNOPSIS
+
+ # in your app.psgi
+ use Plack::Builder;
+
+ builder {
+ enable "LighttpdScriptNameFix";
+ $app;
+ };
+
+ # Or from the command line
+ plackup -s FCGI -e 'enable "LighttpdScriptNameFix"' /path/to/app.psgi
+
+=head1 DESCRIPTION
+
+This middleware fixes wrong C<SCRIPT_NAME> and C<PATH_INFO> set by
+lighttpd when you mount your app under the root path ("/"). If you use
+lighttpd 1.4.23 or later you can instead enable C<fix-root-scriptname>
+flag inside C<fastcgi.server> instead of using this middleware.
+
+=head1 CONFIGURATION
+
+=over 4
+
+=item script_name
+
+Even with C<fix-root-scriptname>, lighttpd I<still> sets weird
+C<SCRIPT_NAME> and C<PATH_INFO> if you mount your application at C<"">
+or something that ends with C</>. Setting C<script_name> option tells
+the middleware how to reconstruct the new correct C<SCRIPT_NAME> and
+C<PATH_INFO>.
+
+If you mount the app under C</something/>, you should set:
+
+ enable "LighttpdScriptNameFix", script_name => "/something";
+
+and when a request for C</something/a/b?param=1> comes, C<SCRIPT_NAME>
+becomes C</something> and C<PATH_INFO> becomes C</a/b>.
+
+C<script_name> option is set to empty by default, which means all the
+request path is set to C<PATH_INFO> and it behaves like your fastcgi
+application is mounted in the root path.
+
+=back
+
+=head1 AUTHORS
+
+Yury Zavarin
+
+Tatsuhiko Miyagawa
+
+=head1 SEE ALSO
+
+L<Plack::Handler::FCGI>
+L<http://github.com/miyagawa/Plack/issues#issue/68>
+L<https://redmine.lighttpd.net/issues/729>
+
+=cut
+
@@ -6,6 +6,16 @@ use parent qw(Plack::Middleware);
use Scalar::Util qw(blessed);
use Plack::Util;
+sub wrap {
+ my($self, $app) = @_;
+
+ unless (ref $app eq 'CODE' or overload::Method($app, '&{}')) {
+ Carp::croak("PSGI app should be a code reference: $app");
+ }
+
+ $self->SUPER::wrap($app);
+}
+
sub call {
my $self = shift;
my $env = shift;
@@ -79,7 +89,7 @@ sub validate_res {
my $croak = $streaming ? \&Carp::confess : \&Carp::croak;
unless (ref($res) and ref($res) eq 'ARRAY' || ref($res) eq 'CODE') {
- $croak->('response should be arrayref or coderef');
+ $croak->('response should be array ref or code ref');
}
if (ref $res eq 'CODE') {
@@ -127,12 +137,18 @@ Plack::Middleware::Lint - Validate request and response
my $app = sub { ... }; # your app or middleware
$app = Plack::Middleware::Lint->wrap($app);
+ # Or from plackup
+ plackup -e 'enable "Lint"' myapp.psgi
+
=head1 DESCRIPTION
Plack::Middleware::Lint is a middleware component to validate request
-and response environment. You are strongly suggested to use enable
-this middleware when you develop a framework adapter or a new server
-that implements PSGI interface.
+and response environment formats. You are strongly suggested to use
+this middleware when you develop a new framework adapter or a new PSGI
+web server that implements the PSGI interface.
+
+This middleware is enabled by default when you run plackup or other
+launcher tools with the default environment I<development> value.
=head1 AUTHOR
@@ -140,5 +156,9 @@ Tatsuhiko Miyagawa
Tokuhiro Matsuno
+=head1 SEE ALSO
+
+L<Plack>
+
=cut
@@ -21,6 +21,8 @@ sub call {
$env->{'psgix.logger'} = sub {
my $args = shift;
my $level = $args->{level};
+ local $Log::Log4perl::caller_depth
+ = $Log::Log4perl::caller_depth + 1;
$self->logger->$level($args->{message});
};
@@ -5,11 +5,12 @@ use parent qw/Plack::Middleware/;
use Devel::StackTrace;
use Devel::StackTrace::AsHTML;
use Try::Tiny;
+use Plack::Util::Accessor qw( force );
our $StackTraceClass = "Devel::StackTrace";
# Optional since it needs PadWalker
-if (try { require Devel::StackTrace::WithLexicals; 1 }) {
+if ($ENV{PLACK_STACKTRACE_LEXICALS} && try { require Devel::StackTrace::WithLexicals; 1 }) {
$StackTraceClass = "Devel::StackTrace::WithLexicals";
}
@@ -22,13 +23,14 @@ sub call {
die @_;
};
- my $res = try { $self->app->($env) };
+ my $caught;
+ my $res = try { $self->app->($env) } catch { $caught = $_ };
- if ($trace && (!$res or $res->[0] == 500)) {
+ if ($trace && ($caught || $self->{force} && ref $res eq 'ARRAY' && $res->[0] == 500) ) {
if (($env->{HTTP_ACCEPT} || '*/*') =~ /html/) {
- $res = [500, ['Content-Type' => 'text/html; charset=utf-8'], [ $trace->as_html ]];
+ $res = [500, ['Content-Type' => 'text/html; charset=utf-8'], [ utf8_safe($trace->as_html) ]];
} else {
- $res = [500, ['Content-Type' => 'text/plain; charset=utf-8'], [ $trace->as_string ]];
+ $res = [500, ['Content-Type' => 'text/plain; charset=utf-8'], [ utf8_safe($trace->as_string) ]];
}
}
@@ -40,6 +42,22 @@ sub call {
return $res;
}
+sub utf8_safe {
+ my $str = shift;
+
+ # NOTE: I know messing with utf8:: in the code is WRONG, but
+ # because we're running someone else's code that we can't
+ # guarnatee which encoding an exception is encoded, there's no
+ # better way than doing this. The latest Devel::StackTrace::AsHTML
+ # (0.08 or later) encodes high-bit chars as HTML entities, so this
+ # path won't be executed.
+ if (utf8::is_utf8($str)) {
+ utf8::encode($str);
+ }
+
+ $str;
+}
+
1;
__END__
@@ -50,7 +68,7 @@ Plack::Middleware::StackTrace - Displays stack trace when your app dies
=head1 SYNOPSIS
- enable "Plack::Middleware::StackTrace";
+ enable "StackTrace";
=head1 DESCRIPTION
@@ -58,11 +76,37 @@ This middleware catches exceptions (run-time errors) happening in your
application and displays nice stack trace screen.
This middleware is enabled by default when you run L<plackup> in the
-default development mode.
+default I<development> mode.
+
+You're recommended to use this middleware during the development and
+use L<Plack::Middleware::HTTPExceptions> in the deployment mode as a
+replacement, so that all the exceptions thrown from your application
+still get caught and rendered as a 500 error response, rather than
+crashing the web server.
+
+Catching errors in streaming response is not supported.
=head1 CONFIGURATION
-No configuration option is available.
+=over 4
+
+=item force
+
+ enable "StackTrace", force => 1;
+
+Force display the stack trace when an error occurs within your
+application and the response code from your application is
+500. Defaults to off.
+
+The use case of this option is that when your framework catches all
+the exceptions in the main handler and returns all failures in your
+code as a normal 500 PSGI error response. In such cases, this
+middleware would never have a chance to display errors because it
+can't tell if it's an application error or just random C<eval> in your
+code. This option enforces the middleware to display stack trace even
+if it's not the direct error thrown by the application.
+
+=back
=head1 AUTHOR
@@ -72,7 +116,7 @@ Tatsuhiko Miyagawa
=head1 SEE ALSO
-L<Devel::StackTrace::AsHTML> L<Plack::Middleware>
+L<Devel::StackTrace::AsHTML> L<Plack::Middleware> L<Plack::Middleware::HTTPExceptions>
=cut
@@ -2,7 +2,7 @@ package Plack::Request;
use strict;
use warnings;
use 5.008_001;
-our $VERSION = '0.9920';
+our $VERSION = '0.9932';
$VERSION = eval $VERSION;
use HTTP::Headers;
@@ -46,6 +46,9 @@ sub secure { $_[0]->scheme eq 'https' }
sub body { $_[0]->env->{'psgi.input'} }
sub input { $_[0]->env->{'psgi.input'} }
+sub content_length { $_[0]->env->{CONTENT_LENGTH} }
+sub content_type { $_[0]->env->{CONTENT_TYPE} }
+
sub session { $_[0]->env->{'psgix.session'} }
sub session_options { $_[0]->env->{'psgix.session.options'} }
sub logger { $_[0]->env->{'psgix.logger'} }
@@ -90,9 +93,9 @@ sub content {
$self->_parse_request_body;
}
- my $fh = $self->input or return '';
- my $cl = $self->content_length or return '';
- $fh->read(my($content), $self->content_length || 0, 0);
+ my $fh = $self->input or return '';
+ my $cl = $self->env->{CONTENT_LENGTH} or return'';
+ $fh->read(my($content), $cl, 0);
$fh->seek(0, 0);
return $content;
@@ -116,10 +119,8 @@ sub headers {
}
$self->{headers};
}
-# shortcut
+
sub content_encoding { shift->headers->content_encoding(@_) }
-sub content_length { shift->headers->content_length(@_) }
-sub content_type { shift->headers->content_type(@_) }
sub header { shift->headers->header(@_) }
sub referer { shift->headers->referer(@_) }
sub user_agent { shift->headers->user_agent(@_) }
@@ -252,7 +253,6 @@ sub _parse_request_body {
return;
}
- # Do not use ->content_type to get multipart boundary correctly
my $body = HTTP::Body->new($ct, $cl);
my $input = $self->input;
@@ -1,7 +1,7 @@
package Plack::Response;
use strict;
use warnings;
-our $VERSION = '0.9920';
+our $VERSION = '0.9932';
$VERSION = eval $VERSION;
use Plack::Util::Accessor qw(body status);
@@ -50,7 +50,8 @@ sub parse_options {
'r|reload' => sub { $self->{loader} = "Restarter" },
'R|Reload=s' => sub { $self->{loader} = "Restarter"; $self->loader->watch(split ",", $_[1]) },
'L|loader=s' => \$self->{loader},
- "h|help", => \$self->{help},
+ "h|help" => \$self->{help},
+ "v|version" => \$self->{version},
);
my(@options, @argv);
@@ -60,8 +61,10 @@ sub parse_options {
$v[0] =~ tr/-/_/;
if (@v == 2) {
push @options, @v;
+ } elsif ($v[0] =~ s/^(disable|enable)_//) {
+ push @options, $v[0], $1 eq 'enable';
} else {
- push @options, @v, shift @ARGV;
+ push @options, $v[0], shift @ARGV;
}
} else {
push @argv, $_;
@@ -104,6 +107,14 @@ sub mangle_host_port_socket {
return host => $host, port => $port, listen => \@listen, socket => $socket;
}
+sub version_cb {
+ my $self = shift;
+ $self->{version_cb} || sub {
+ require Plack;
+ print "Plack $Plack::VERSION\n";
+ };
+}
+
sub setup {
my $self = shift;
@@ -112,6 +123,11 @@ sub setup {
Pod::Usage::pod2usage(0);
}
+ if ($self->{version}) {
+ $self->version_cb->();
+ exit;
+ }
+
lib->import(@{$self->{includes}}) if @{$self->{includes}};
if ($self->{eval}) {
@@ -160,21 +176,28 @@ sub watch {
if $self->{loader} eq 'Restarter';
}
+sub apply_middleware {
+ my($self, $app, $class, @args) = @_;
+
+ my $mw_class = Plack::Util::load_class($class, 'Plack::Middleware');
+ build { $mw_class->wrap($_[0], @args) } $app;
+}
+
sub prepare_devel {
my($self, $app) = @_;
- require Plack::Middleware::StackTrace;
- require Plack::Middleware::AccessLog;
- $app = build { Plack::Middleware::StackTrace->wrap($_[0]) } $app;
+ $app = $self->apply_middleware($app, 'Lint');
+ $app = $self->apply_middleware($app, 'StackTrace');
unless ($ENV{GATEWAY_INTERFACE}) {
- $app = build { Plack::Middleware::AccessLog->wrap($_[0], logger => sub { print STDERR @_ }) } $app;
+ $app = $self->apply_middleware($app, 'AccessLog', logger => sub { print STDERR @_ });
}
push @{$self->{options}}, server_ready => sub {
my($args) = @_;
- my $name = $args->{server_software} || ref($args); # $args is $server
- my $host = $args->{host} || 0;
- print STDERR "$name: Accepting connections at http://$host:$args->{port}/\n";
+ my $name = $args->{server_software} || ref($args); # $args is $server
+ my $host = $args->{host} || 0;
+ my $proto = $args->{proto} || 'http';
+ print STDERR "$name: Accepting connections at $proto://$host:$args->{port}/\n";
};
$app;
@@ -1,6 +1,6 @@
package Plack::Server::ServerSimple;
use strict;
-our $VERSION = '0.9920';
+our $VERSION = '0.9932';
$VERSION = eval $VERSION;
use parent qw(Plack::Handler::HTTP::Server::Simple);
@@ -19,6 +19,10 @@ Plack::Server::Standalone::Prefork - DEPRECATED
=head1 DESCRIPTION
-B<This module is deprecated>. See L<Plack::Handler::Standalone>.
+B<This module is deprecated>.
+
+=head1 SEE ALSO
+
+L<HTTP::Server::PSGI> L<Starman> L<Starlet>
=cut
@@ -161,15 +161,15 @@ our @TEST = (
'bigger file',
sub {
my $cb = shift;
- my $res = $cb->(GET "http://127.0.0.1/kyoto.jpg");
+ my $res = $cb->(GET "http://127.0.0.1/baybridge.jpg");
is $res->code, 200;
is $res->header('content_type'), 'image/jpeg';
- is length $res->content, 2397701;
- is Digest::MD5::md5_hex($res->content), '9c6d7249a77204a88be72e9b2fe279e8';
+ is length $res->content, 79838;
+ is Digest::MD5::md5_hex($res->content), '983726ae0e4ce5081bef5fb2b7216950';
},
sub {
my $env = shift;
- open my $fh, '<', "$share_dir/kyoto.jpg";
+ open my $fh, '<', "$share_dir/baybridge.jpg";
binmode $fh;
return [
200,
@@ -308,12 +308,7 @@ our @TEST = (
},
],
[
- # PEP-333 says:
- # If the iterable returned by the application has a close() method,
- # the server or gateway must call that method upon completion of the
- # current request, whether the request was completed normally, or
- # terminated early due to an error.
- 'call close after read file-like',
+ 'call close after read IO::Handle-like',
sub {
my $cb = shift;
my $res = $cb->(GET "http://127.0.0.1/call_close");
@@ -322,14 +317,13 @@ our @TEST = (
sub {
my $env = shift;
{
- package CalledClose;
our $closed = -1;
- sub new { $closed = 0; my $i=0; bless \$i, 'CalledClose' }
- sub getline {
+ sub CalledClose::new { $closed = 0; my $i=0; bless \$i, 'CalledClose' }
+ sub CalledClose::getline {
my $self = shift;
return $$self++ < 4 ? $$self : undef;
}
- sub close { ::ok(1, 'closed') if defined &::ok }
+ sub CalledClose::close { ::ok(1, 'closed') if defined &::ok }
}
return [
200,
@@ -679,13 +673,29 @@ sub test_app_handler {
1;
__END__
+=head1 NAME
+
+Plack::Test::Suite - Test suite for Plack handlers
+
=head1 SYNOPSIS
- # TBD See t/Plack-Servet/*.t for now
+ use Test::More;
+ use Plack::Test::Suite;
+ Plack::Test::Suite->run_server_tests('Your::Handler');
+ done_testing;
=head1 DESCRIPTION
-Plack::Test::Suite is a test suite to test a new PSGI server implementation.
+Plack::Test::Suite is a test suite to test a new PSGI server
+implementation. It automatically loads a new handler environment and
+uses LWP to send HTTP requests to the local server to make sure your
+handler implements the PSGI specification correctly.
+
+Note that the handler name doesn't include the C<Plack::Handler::>
+prefix, i.e. if you have a new Plack handler Plack::Handler::Foo, your
+test script would look like:
+
+ Plack::Test::Suite->run_server_tests('Foo');
=head1 AUTHOR
@@ -3,7 +3,7 @@ package Plack;
use strict;
use warnings;
use 5.008_001;
-our $VERSION = '0.9920';
+our $VERSION = '0.9932';
$VERSION = eval $VERSION;
1;
@@ -27,16 +27,23 @@ PSGI and Plack are and why we need them.
=head2 Plack::Handler
L<Plack::Handler> and its subclasses contains adapters for web
-servers. We have adapters for Standalone, CGI, FCGI, Apache, AnyEvent,
-Coro, Danga::Socket and many server environments that you can run PSGI
-applications on.
+servers. We have adapters for the built-in standalone web server
+L<HTTP::Server::PSGI>, L<CGI|Plack::Handler::CGI>,
+L<FCGI|Plack::Handler::FCGI>, L<Apache1|Plack::Handler::Apache1>,
+L<Apache2|Plack::Handler::Apache2>,
+L<Net::FastCGI|Plack::Handler::Net::FastCGI> and
+L<HTTP::Server::Simple|Plack::Handler::HTTP::Server::Simple> included
+in the core Plack distribution.
+
+There are also many HTTP server implementations on CPAN that has Plack
+handlers.
See L<Plack::Handler> how to write your own adapters.
=head2 Plack::Loader
-L<Plack::Loader> is a loader to load one of Plack::Server adapters and
-run PSGI application code reference with it.
+L<Plack::Loader> is a loader to load one of L<Plack::Handler> adapters
+and run PSGI application code reference with it.
=head2 Plack::Util
@@ -120,8 +127,13 @@ L<Plack::Test::Suite> is a test suite to test a new PSGI server backend.
=head2 Patches and Bug Fixes
Small patches and bug fixes can be either submitted via nopaste on IRC
-L<irc://irc.perl.org/#plack> or email. You could also fork on github
-(http://github.com/miyagawa/Plack) to make larger fixes.
+L<irc://irc.perl.org/#plack> or L<the github issue
+tracker|http://github.com/miyagawa/Plack/issues>. Forking on
+L<github|http://github.com/miyagawa/Plack> is another good way if you
+intend to make larger fixes.
+
+See also L<http://contributing.appspot.com/plack> when you think this
+document is terribly outdated.
=head2 Module Namespaces
@@ -148,14 +160,14 @@ framework. It's like naming your application under CGI:: namespace if
it's supposed to run on CGI and that is a really bad choice and
confuse people.
-=head1 COPYRIGHT
-
-Copyright 2009- Tatsuhiko Miyagawa
-
=head1 AUTHOR
Tatsuhiko Miyagawa
+=head1 COPYRIGHT
+
+Copyright 2009-2010 Tatsuhiko Miyagawa
+
=head1 CONTRIBUTORS
Yuval Kogman (nothingmuch)
@@ -164,7 +176,7 @@ Tokuhiro Matsuno (tokuhirom)
Kazuhiro Osawa (Yappo)
-Kzzuho Oku
+Kazuho Oku
Florian Ragwitz (rafl)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!perl
use strict;
use lib "lib";
use Plack::Runner;
@@ -96,7 +96,7 @@ TCP socket.
Select a specific implementation to run on using the C<PLACK_SERVER>
environment variable or use the C<-s> or C<--server> flag which will
-be prefered over the environment variable if present.
+be preferred over the environment variable if present.
=item -S, --socket
@@ -137,8 +137,8 @@ environment setting the application is running on.
The value can be anything but commonly used ones are C<development>,
C<deployment> and C<test>.
-If it's set to C<development>, following middleware is enabled by
-default: L<AccessLog>, L<StackTrace>.
+If it's set to C<development>, following middleware components are
+enabled by default: I<AccessLog>, I<StackTrace> and I<Lint>.
=item -r, --reload
@@ -158,12 +158,16 @@ separated by comma (C<,>).
Specify the server loading subclass that implements how to run the
server. Available options are I<Plack::Loader> (default), I<Restarter>
-(automatically set when C<-r> or C<-R> is used) and I<Shotgun>.
+(automatically set when C<-r> or C<-R> is used), I<Delayed> and
+I<Shotgun>.
+
+See L<Plack::Loader::Delayed> and L<Plack::Loader::Shotgun> when to
+use those loader types.
=back
Other options that starts with C<--> are passed through to the backend
-server. See each Plack::Server backend documentations to see which
+server. See each Plack::Handler backend documentations to see which
options are available.
=head1 SEE ALSO
diff --git a/var/tmp/source/MIYAGAWA/Plack-0.9932/Plack-0.9932/share/baybridge.jpg b/var/tmp/source/MIYAGAWA/Plack-0.9932/Plack-0.9932/share/baybridge.jpg
new file mode 100644
index 00000000..484bcda9
Binary files /dev/null and b/var/tmp/source/MIYAGAWA/Plack-0.9932/Plack-0.9932/share/baybridge.jpg differ
diff --git a/var/tmp/source/MIYAGAWA/Plack-0.9920/Plack-0.9920/share/kyoto.jpg b/var/tmp/source/MIYAGAWA/Plack-0.9920/Plack-0.9920/share/kyoto.jpg
deleted file mode 100644
index 05ad1992..00000000
Binary files a/var/tmp/source/MIYAGAWA/Plack-0.9920/Plack-0.9920/share/kyoto.jpg and /dev/null differ
@@ -53,11 +53,18 @@ sub test_lighty_external (&@) {
plan skip_all => 'Please set LIGHTTPD_BIN to the path to lighttpd'
unless $lighttpd_bin && -x $lighttpd_bin;
+ my $ver = (`$lighttpd_bin -v` =~ m!lighttpd[-/]1.(\d+\.\d+)!)[0];
+ if ($ver < 4.17) {
+ plan skip_all => "Too old lighttpd (1.$ver), known to be broken";
+ }
+
+ diag "Testing with lighttpd 1.$ver";
+
my $tmpdir = File::Temp::tempdir( CLEANUP => 1 );
test_tcp(
client => sub {
- $callback->($lighty_port, $fcgi_port);
+ $callback->($lighty_port, $fcgi_port, ($ver && $ver < 4.23));
warn `cat $tmpdir/error.log` if $ENV{DEBUG};
},
server => sub {
@@ -98,11 +105,12 @@ server.port = $port
# HTTP::Engine app specific fcgi setup
fastcgi.server = (
- "" => ((
+ "/" => ((
"check-local" => "disable",
"host" => "127.0.0.1",
"port" => $fcgiport,
"idle-timeout" => 20,
+ "fix-root-scriptname" => "enable", # for 1.4.23 or later
))
)
END
@@ -12,24 +12,32 @@ my $fcgi_port;
test_lighty_external(
sub {
- ($lighty_port, $fcgi_port) = @_;
- Plack::Test::Suite->run_server_tests(\&run_server, $fcgi_port, $lighty_port);
+ ($lighty_port, $fcgi_port, my $needs_fix) = @_;
+ Plack::Test::Suite->run_server_tests(run_server_cb($needs_fix), $fcgi_port, $lighty_port);
done_testing();
}
);
-sub run_server {
- my($port, $app) = @_;
+sub run_server_cb {
+ my $needs_fix = shift;
- $| = 0; # Test::Builder autoflushes this. reset!
+ require Plack::Middleware::LighttpdScriptNameFix;
+ return sub {
+ my($port, $app) = @_;
- my $server = Plack::Handler::FCGI->new(
- host => '127.0.0.1',
- port => $port,
- manager => '',
- keep_stderr => 1,
- );
- $server->run($app);
+ note "Applying LighttpdScriptNameFix" if $needs_fix;
+ $app = Plack::Middleware::LighttpdScriptNameFix->wrap($app) if $needs_fix;
+
+ $| = 0; # Test::Builder autoflushes this. reset!
+
+ my $server = Plack::Handler::FCGI->new(
+ host => '127.0.0.1',
+ port => $port,
+ manager => '',
+ keep_stderr => 1,
+ );
+ $server->run($app);
+ };
}
@@ -0,0 +1,33 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::Requires { 'Net::FastCGI' => 0.11, 'FCGI::Client' => 0.04 };
+use Plack::Handler::Net::FastCGI;
+use Test::TCP;
+use Plack::Test::Suite;
+use t::FCGIUtils;
+
+my $http_port;
+my $fcgi_port;
+
+test_fcgi_standalone(
+ sub {
+ ($http_port, $fcgi_port) = @_;
+ Plack::Test::Suite->run_server_tests(\&run_server, $fcgi_port, $http_port);
+ done_testing();
+ }
+);
+
+sub run_server {
+ my($port, $app) = @_;
+
+ $| = 0; # Test::Builder autoflushes this. reset!
+
+ my $server = Plack::Handler::Net::FastCGI->new(
+ host => '127.0.0.1',
+ port => $port,
+ );
+ $server->run($app);
+}
+
+
@@ -1,15 +0,0 @@
-use strict;
-use warnings;
-use Test::More;
-use Test::Requires {
- 'HTTP::Parser::XS' => 0,
- 'Parallel::Prefork' => 0.04,
-};
-
-use FindBin;
-use Plack;
-use Plack::Test::Suite;
-
-Plack::Test::Suite->run_server_tests('Standalone', undef, undef, max_workers => 10);
-done_testing();
-
@@ -1,4 +1,5 @@
use strict;
+no warnings 'redefine';
use Test::More;
use Plack::Loader;
@@ -0,0 +1,28 @@
+use strict;
+use Test::More;
+use Plack::Loader;
+
+my $compiled;
+
+my $builder = sub {
+ $compiled = 1;
+ my $app = sub {
+ return [ 200, [], [ "Hi" ] ];
+ };
+};
+
+# The following eval might not fail if you set PLACK_SEVER
+delete $ENV{PLACK_SERVER};
+
+eval {
+ my $loader = Plack::Loader::Delayed->new;
+ $loader->preload_app($builder);
+ my $server = $loader->auto;
+ ok(!$compiled);
+};
+
+ok 1 if $@;
+
+done_testing;
+
+
@@ -0,0 +1,62 @@
+use strict;
+use Test::Requires qw(IO::Handle::Util);
+
+package MyComponent;
+use parent 'Plack::Component';
+use Plack::Util::Accessor qw( res cb );
+
+sub call { return $_[0]->response_cb( $_[0]->res, $_[0]->cb ); }
+
+package main;
+use IO::Handle::Util qw(:io_from);
+use HTTP::Request::Common;
+use Test::More;
+use Plack::Test;
+
+# Various kinds of PSGI responses.
+sub generate_responses {
+ [200, ['Content-Type' => 'text/plain'], ['Hello']],
+ [200, ['Content-Type' => 'text/plain'], io_from_array ['Hello']],
+ sub { $_[0]->([ 200, ['Content-Type' => 'text/plain'], ['Hello'] ]) },
+ sub {
+ my $writer = $_[0]->([ 200, ['Content-Type' => 'text/plain'] ]);
+ $writer->write( 'Hello' );
+ $writer->close;
+ },
+}
+
+# $body filters can return undef with no warnings.
+for my $res ( generate_responses ) {
+ my @warns;
+ local $SIG{__WARN__} = sub { push @warns, @_ };
+
+ my $app = MyComponent->new(
+ res => $res, cb => sub { sub { $_[0] } },
+ );
+ test_psgi( $app, sub { $_[0]->(GET '/') } );
+
+ is_deeply \@warns, [];
+}
+
+for my $res ( generate_responses ) {
+ my $app = MyComponent->new(
+ res => $res, cb => sub {
+ my $done;
+ sub {
+ return if $done;
+ if (defined $_[0]) {
+ return $_[0];
+ } else {
+ $done = 1;
+ return 'END';
+ }
+ },
+ },
+ );
+ test_psgi( $app, sub {
+ my $res = $_[0]->(GET '/');
+ is $res->content, 'HelloEND';
+ } );
+}
+
+done_testing;
@@ -10,11 +10,11 @@ my @tests = (
},
{
app => sub {
- open my $fh, "<", "share/kyoto.jpg";
+ open my $fh, "<", "share/baybridge.jpg";
[ 200, [ 'Content-Type' => 'image/jpeg' ], $fh ];
},
env => { REQUEST_METHOD => 'GET' },
- headers => [ 'Content-Type' => 'image/jpeg', 'Content-Length' => 2397701 ],
+ headers => [ 'Content-Type' => 'image/jpeg', 'Content-Length' => 79838 ],
},
{
app => sub {
@@ -17,16 +17,6 @@ my @app = (
);
};
},
- sub {
- return sub {
- my $respond = shift;
- my $writer = $respond->(
- [ 200, [ 'Content-Type' => 'application/json' ] ],
- );
- $writer->write( $json );
- $writer->close;
- };
- },
);
for my $app ( @app ) {
@@ -1,6 +1,6 @@
use strict;
use Plack::Test;
-use Test::Requires qw(Log::Dispatch::Array);
+use Test::Requires { 'Log::Dispatch' => 2.25, 'Log::Dispatch::Array' => 1.001 };
use Test::More;
use Plack::Middleware::LogDispatch;
@@ -0,0 +1,39 @@
+use strict;
+use warnings;
+use Test::More;
+use Plack::Middleware::StackTrace;
+use Plack::Test;
+use HTTP::Request::Common;
+
+my $app = sub {
+ eval { die "Blah" };
+
+ return [ 500, [ 'Content-Type', 'text/html' ], [ "Fancy Error" ] ];
+};
+
+my $default_app = Plack::Middleware::StackTrace->wrap($app);
+
+test_psgi $default_app, sub {
+ my $cb = shift;
+
+ my $req = GET "/";
+ my $res = $cb->($req);
+
+ is $res->code, 500;
+ like $res->content, qr/Fancy Error/;
+};
+
+my $force_app = Plack::Middleware::StackTrace->wrap($app, force => 1);
+
+test_psgi $force_app, sub {
+ my $cb = shift;
+
+ my $req = GET "/";
+ my $res = $cb->($req);
+
+ is $res->code, 500;
+ like $res->content, qr/Blah/;
+};
+
+done_testing;
+
@@ -0,0 +1,30 @@
+use strict;
+use warnings;
+use Test::More;
+use Plack::Middleware::StackTrace;
+use Plack::Test;
+use HTTP::Request::Common;
+
+my $app = sub {
+ eval { require DooBar };
+
+ return sub {
+ my $respond = shift;
+ $respond->([ 200, [ "Content-Type", "text/plain" ], [ "Hello World" ] ]);
+ };
+};
+
+$app = Plack::Middleware::StackTrace->wrap($app);
+
+test_psgi $app, sub {
+ my $cb = shift;
+
+ my $req = GET "/";
+ my $res = $cb->($req);
+
+ ok $res->is_success;
+ like $res->content, qr/Hello World/;
+};
+
+done_testing;
+
@@ -0,0 +1,28 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::Requires { 'Devel::StackTrace::AsHTML' => 0.08 };
+use Plack::Middleware::StackTrace;
+use Plack::Test;
+use HTTP::Request::Common;
+
+$Plack::Test::Impl = "Server";
+my $app = Plack::Middleware::StackTrace->wrap(sub { die "Foo \x{30c6}" });
+
+test_psgi $app, sub {
+ my $cb = shift;
+
+ my $req = GET "/";
+ $req->header(Accept => "text/html,*/*");
+ my $res = $cb->($req);
+
+ like $res->content, qr/Foo テ/;
+
+ $req = GET "/";
+ $res = $cb->($req);
+ is $res->code, 500;
+ like $res->content, qr/Foo/;
+};
+
+done_testing;
+
@@ -0,0 +1,33 @@
+use strict;
+use Test::More;
+use Plack::App::URLMap;
+use Plack::Test;
+use HTTP::Request::Common;
+
+$Plack::Test::Impl = "Server";
+
+my $make_app = sub {
+ my $name = shift;
+ sub {
+ my $env = shift;
+ my $body = join "|", $name, $env->{SCRIPT_NAME}, $env->{PATH_INFO};
+ return [ 200, [ 'Content-Type' => 'text/plain' ], [ $body ] ];
+ };
+};
+
+my $app1 = $make_app->("app1");
+my $app2 = $make_app->("app2");
+
+my $app = Plack::App::URLMap->new;
+$app->map("http://127.0.0.1/" => $app1);
+$app->map("/" => $app2);
+
+test_psgi app => $app, client => sub {
+ my $cb = shift;
+
+ my $res;
+ $res = $cb->(GET "http://127.0.0.1/");
+ is $res->content, 'app1||/';
+};
+
+done_testing;
@@ -5,7 +5,7 @@ use Plack::Request;
use Plack::Test;
use HTTP::Request::Common;
-my $file = "share/kyoto.jpg";
+my $file = "share/baybridge.jpg";
my @backends = qw( Server MockHTTP );
sub flip_backend { $Plack::Test::Impl = shift @backends }
@@ -14,7 +14,7 @@ my $app = sub {
my $req = Plack::Request->new(shift);
is $req->uploads->{image}->size, -s $file;
is $req->uploads->{image}->content_type, 'image/jpeg';
- is $req->uploads->{image}->basename, 'kyoto.jpg';
+ is $req->uploads->{image}->basename, 'baybridge.jpg';
$req->new_response(200)->finalize;
};
@@ -18,6 +18,8 @@ is_deeply p('-l', ':80'),
{ host => undef, port => 80, listen => [ ':80' ], socket => undef };
is_deeply p('-l', '10.0.0.1:80', '-l', 'unix.sock'),
{ host => '10.0.0.1', port => 80, listen => [ '10.0.0.1:80', 'unix.sock' ], socket => 'unix.sock' };
+is_deeply p('-l', ':80', '--disable-foo', '--enable-bar'),
+ { host => undef, port => 80, listen => [ ':80' ], socket => undef, foo => '', bar => 1 };
done_testing;